<?php

class Wechat {

    const API_BASE_URL_PREFIX = 'https://api.weixin.qq.com'; //以下API接口URL需要使用此前缀
    const OAUTH_PREFIX = 'https://open.weixin.qq.com/connect/oauth2';
    const OAUTH_AUTHORIZE_URL = '/authorize?';
    const OAUTH_TOKEN_URL = '/sns/oauth2/access_token?';
    const OAUTH_REFRESH_URL = '/sns/oauth2/refresh_token?';
    const OAUTH_USERINFO_URL = '/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN';
    const OAUTH_AUTH_URL = '/sns/auth?';
    const API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin';
    const AUTH_URL = '/token?grant_type=client_credential&';
    const MESSAGE_TEMPLATE='/cgi-bin/message/template/send?';
    const OAUTH_ISFOLLOW = '/cgi-bin/user/info?';


    private $token;
    private $encodingAesKey;
    private $encrypt_type;
    private $appid;
    private $appsecret;
    private $access_token;
    private $jsapi_ticket;
    private $api_ticket;
    private $user_token;
    private $partnerid;
    private $partnerkey;
    private $paysignkey;
    private $postxml;
    private $_msg;
    private $_funcflag = false;
    private $_receive;
    private $_text_filter = true;
    public $debug = false;
    public $errCode = 40001;
    public $errMsg = "no access55";
    public $logcallback;

    public function __construct($options) {
        $this->token = isset($options['token']) ? $options['token'] : '';
        $this->encodingAesKey = isset($options['encodingaeskey']) ? $options['encodingaeskey'] : '';
        $this->appid = isset($options['appid']) ? $options['appid'] : '';
        $this->appsecret = isset($options['appsecret']) ? $options['appsecret'] : '';
        $this->debug = isset($options['debug']) ? $options['debug'] : false;
        $this->logcallback = isset($options['logcallback']) ? $options['logcallback'] : false;
    }

    /**
     * For weixin server validation
     */
    private function checkSignature($str = '') {
        $signature = isset($_GET["signature"]) ? $_GET["signature"] : '';
        $signature = isset($_GET["msg_signature"]) ? $_GET["msg_signature"] : $signature; //如果存在加密验证则用加密验证段
        $timestamp = isset($_GET["timestamp"]) ? $_GET["timestamp"] : '';
        $nonce = isset($_GET["nonce"]) ? $_GET["nonce"] : '';

        $token = $this->token;

        $tmpArr = array($token, $timestamp, $nonce, $str);
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode($tmpArr);
        $tmpStr = sha1($tmpStr);

        if ($tmpStr == $signature) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * For weixin server validation
     * @param bool $return 是否返回
     */
    public function valid($return = false) {
        $encryptStr = "";
        if ($_SERVER['REQUEST_METHOD'] == "POST") {
            $postStr = file_get_contents("php://input");
            $array = (array) simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $this->encrypt_type = isset($_GET["encrypt_type"]) ? $_GET["encrypt_type"] : '';

            if ($this->encrypt_type == 'aes') { //aes加密 
                $encryptStr = $array['Encrypt'];
                $pc = new Prpcrypt($this->encodingAesKey);
                $array = $pc->decrypt($encryptStr, $this->appid);
                if (!isset($array[0]) || ($array[0] != 0)) {
                    if (!$return) {
                        die('decrypt error!');
                    } else {
                        return false;
                    }
                }
                $this->postxml = $array[1];
                if (!$this->appid)
                    $this->appid = $array[2]; //为了没有appid的订阅号。
            } else {
                $this->postxml = $postStr;
            }
        } elseif (isset($_GET["echostr"])) {
            $echoStr = $_GET["echostr"];
            if ($return) {
                if ($this->checkSignature()) {
                    return $echoStr;
                } else
                    return false;
            } else {
                if ($this->checkSignature()) {
                    die($echoStr);
                } else {
                    die('no access111111');
                }
            }
        }

        if (!$this->checkSignature($encryptStr)) {
            if ($return)
                return false;
            else {
                die('no access2');
            }
        }
        return true;
    }

    /**
     * oauth 授权跳转接口
     * @param string $callback 回调URI
     * @return string
     */
    public function getOauthRedirect($callback, $state = '', $scope = 'snsapi_userinfo') {
        return self::OAUTH_PREFIX . self::OAUTH_AUTHORIZE_URL . 'appid=' . $this->appid . '&redirect_uri=' . urlencode($callback) . '&response_type=code&scope=' . $scope . '&state=' . $state . '#wechat_redirect';
    }

    /**
     * 通过code获取Access Token
     * @return array {access_token,expires_in,refresh_token,openid,scope}
     */
    public function getOauthAccessToken() {
        
        $code = isset($_GET['code']) ? $_GET['code'] : '';
        if (!$code) return false;
        $result = $this->http_get(self::API_BASE_URL_PREFIX . self::OAUTH_TOKEN_URL . 'appid=' . $this->appid . '&secret=' . $this->appsecret . '&code=' . $code . '&grant_type=authorization_code');
         
        if ($result) {
            $json = json_decode($result, true);
            if (!$json || !empty($json['errcode'])) {
                $this->errCode = $json['errcode'];
                $this->errMsg = $json['errmsg'];
                return false;
            }
            $this->user_token = $json['access_token'];
            return $json;
        }
        return false;
    }

    /**
     * 刷新access token并续期
     * @param string $refresh_token
     * @return boolean|mixed
     */
    public function getOauthRefreshToken($refresh_token) {
        $result = $this->http_get(self::API_BASE_URL_PREFIX . self::OAUTH_REFRESH_URL . 'appid=' . $this->appid . '&grant_type=refresh_token&refresh_token=' . $refresh_token);
        if ($result) {
            $json = json_decode($result, true);
            if (!$json || !empty($json['errcode'])) {
                $this->errCode = $json['errcode'];
                $this->errMsg = $json['errmsg'];
                return false;
            }
            $this->user_token = $json['access_token'];
            return $json;
        }
        return false;
    }

    /**
     *  //https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
     * 通过Access Token获取UserInfo
     * @return array {access_token,expires_in,refresh_token,openid,scope}
     */
    public function getUSERINFO($accesstoken, $openid) {
        if (empty($accesstoken))
            return false;
        $url = sprintf(self::API_BASE_URL_PREFIX .self::OAUTH_USERINFO_URL, $accesstoken,$openid);
        $result = $this->http_get($url,'');
        if (!empty($result)) {
            $json = json_decode($result, true);
            return $json;
        }
        return false;
    }


    /**
     * 是否有关注
     * @param string $access_token  array $data
     * @return boolean b
     */
    public function isFollow($openid) {

        if (!$this->access_token && !$this->checkAuth()){
            return false;
        }
        $result = $this->http_get(self::API_BASE_URL_PREFIX . self::OAUTH_ISFOLLOW . 'access_token=' . $this->access_token.'&openid='.$openid.'&lang=zh_CN');
        $result=json_decode($result);
        if (!empty($result)){
            if (isset($result->subscribe) && !empty($result->subscribe)){
                return true;
            }
            else {
                return false;
            }
        }
        return $result;
    }

    /**
     * 发送模版消息
     * @param string $access_token  array $data
     * @return array $json
     */
    public function sendMessageByTemplate($data) {

        if (!$this->access_token && !$this->checkAuth()){
            return false;
        }

        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::MESSAGE_TEMPLATE . 'access_token=' . $this->access_token, json_encode($data));
        if ($result) {
            $json = json_decode($result, true);
            return $json;
        }
        return false;
    }

    /**
     * 获取模版列表
     * @param
     * @return array $json
     */
    public function getMessageTemplate() {
        //https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token=ACCESS_TOKEN
        if (!$this->access_token && !$this->checkAuth()){
            return false;
        }
        $result = $this->http_get(self::API_BASE_URL_PREFIX . '/cgi-bin/template/get_all_private_template?' . 'access_token=' . $this->access_token);

        if ($result) {
            $json = json_decode($result, true);
            if (!$json || !empty($json['errcode'])) {
                $this->errCode = $json['errcode'];
                $this->errMsg = $json['errmsg'];
                return false;
            }
            return $json;
        }
        return false;
    }


    /**
     * GET 请求
     * @param string $url
     */
    private function http_get($url) {
        $oCurl = curl_init();
        if (stripos($url, "https://") !== FALSE) {
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        curl_setopt($oCurl, CURLOPT_URL, $url);
        curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
        //echo 'url:'.$url.'<br/>';
        $sContent = curl_exec($oCurl);
        $aStatus = curl_getinfo($oCurl);
        //echo 'Content:'.$sContent.'<br/>';
        $this->log("GET url:" . $url);
        $this->log("content:" . $sContent);
        curl_close($oCurl);
        if (intval($aStatus["http_code"]) == 200) {
            return $sContent;
        } else {
            return false;
        }
    }

    /**
     * POST 请求
     * @param string $url
     * @param array $param
     * @param boolean $post_file 是否文件上传
     * @return string content
     */
    private function http_post($url, $param, $post_file = false) {
        $oCurl = curl_init();
        if (stripos($url, "https://") !== FALSE) {
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        if (is_string($param) || $post_file) {
            $strPOST = $param;
        } else {
            $aPOST = array();
            foreach ($param as $key => $val) {
                $aPOST[] = $key . "=" . urlencode($val);
            }
            $strPOST = join("&", $aPOST);
        }

        curl_setopt($oCurl, CURLOPT_URL, $url);
        curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($oCurl, CURLOPT_POST, true);
        curl_setopt($oCurl, CURLOPT_POSTFIELDS, $strPOST);
        $sContent = curl_exec($oCurl);
        $aStatus = curl_getinfo($oCurl);
        //echo 'Content:'.$sContent.'<br/>';
        $this->log("POST url:" . $url);
        $this->log("content:" . $sContent);
        curl_close($oCurl);
        if (intval($aStatus["http_code"]) == 200) {
            return $sContent;
        } else {
            return false;
        }
    }

    /**
     * 设置缓存，按需重载
     * @param string $cachename
     * @param mixed $value
     * @param int $expired
     * @return boolean
     */
    protected function setCache($cachename, $value, $expired) {
        //TODO: set cache implementation
        return false;
    }

    /**
     * 获取缓存，按需重载
     * @param string $cachename
     * @return mixed
     */
    protected function getCache($cachename) {
        //TODO: get cache implementation
        return false;
    }

    /**
     * 清除缓存，按需重载
     * @param string $cachename
     * @return boolean
     */
    protected function removeCache($cachename) {
        //TODO: remove cache implementation
        return false;
    }

    /**
     * 获取access_token
     * @param string $appid 如在类初始化时已提供，则可为空
     * @param string $appsecret 如在类初始化时已提供，则可为空
     * @param string $token 手动指定access_token，非必要情况不建议用
     */
    public function checkAuth($appid = '', $appsecret = '', $token = '') {
       
        if (!$appid || !$appsecret) {
            $appid = $this->appid;
            $appsecret = $this->appsecret;
        }

        if ($token) { //手动指定token，优先使用
            $this->access_token = $token;
            return $this->access_token;
        }

        $authname = 'wechat_access_token' . $appid;
        
        if ($rs = $this->getCache($authname)) { 
            $this->access_token = $rs;
            return $rs;
        }
        
        $result = $this->http_get(self::API_URL_PREFIX . self::AUTH_URL . 'appid=' . $appid . '&secret=' . $appsecret);

        if ($result) {
            $json = json_decode($result, true);
            if (!$json || isset($json['errcode'])) {
                $this->errCode = $json['errcode'];
                $this->errMsg = $json['errmsg'];
                return false;
            }
            //var_dump($json);
            $this->access_token = $json['access_token'];
            $expire = $json['expires_in'] ? intval($json['expires_in']) - 100 : 3600;
            $this->setCache($authname, $this->access_token, $expire);
            return $this->access_token;
        }
        return false;
    }

    /**
     * 获取access_token
     * @param string $appid 如在类初始化时已提供，则可为空
     * @param string $appsecret 如在类初始化时已提供，则可为空
     * @param string $token 手动指定access_token，非必要情况不建议用
     */
    public function getAccessToken($appid = '', $appsecret = '', $token = '') {

        if (!$appid || !$appsecret) {
            $appid = $this->appid;
            $appsecret = $this->appsecret;
        }

        if ($token) { //手动指定token，优先使用
            $this->access_token = $token;
            return $this->access_token;
        }

        $authname = 'wechat_access_token' . $appid;

        if ($rs = $this->getCache($authname)) {
            $this->access_token = $rs;
            return $rs;
        }

        $result = $this->http_get(self::API_URL_PREFIX . self::AUTH_URL . 'appid=' . $appid . '&secret=' . $appsecret);

        if ($result) {
            $json = json_decode($result, true);
            if (!$json || isset($json['errcode'])) {
                $this->errCode = $json['errcode'];
                $this->errMsg = $json['errmsg'];
                return false;
            }
            //var_dump($json);
            //$this->access_token = $json['access_token'];
            //$expire = $json['expires_in'] ? intval($json['expires_in']) - 100 : 3600;
            //$this->setCache($authname, $this->access_token, $expire);
            return $json;
        }
        return false;
    }

    /**
     * 删除验证数据
     * @param string $appid
     */
    public function resetAuth($appid = '') {
        if (!$appid)
            $appid = $this->appid;
        $this->access_token = '';
        $authname = 'wechat_access_token' . $appid;
        $this->removeCache($authname);
        return true;
    }

    /**
	 * 获取微信服务器IP地址列表
	 * @return array('127.0.0.1','127.0.0.1')
	 */
	public function getServerIp(){
		if (!$this->access_token && !$this->checkAuth()) return false;
		$result = $this->http_get(self::API_URL_PREFIX.self::CALLBACKSERVER_GET_URL.'access_token='.$this->access_token);
		if ($result)
		{
			$json = json_decode($result,true);
			if (!$json || isset($json['errcode'])) {
				$this->errCode = $json['errcode'];
				$this->errMsg = $json['errmsg'];
				return false;
			}
			return $json['ip_list'];
		}
		return false;
	}

    private function log($message) {
        $serverFilePath = realpath(dirname(__FILE__)); //$_SERVER["SCRIPT_FILENAME"];

        $doc_dir = strrpos($serverFilePath, '/plugin');
        $doc_dir = substr($serverFilePath, 0, $doc_dir + 1);   //project root

        $lfile = $doc_dir . '/logs';
        if (!is_dir($lfile)) {
            $dirs = explode('/', $lfile);
            $dir2 = '';
            foreach ($dirs as $part) {
                $dir2.=$part . '/';
                if (!is_dir($dir2) && strlen($dir2) > 0) {
                    mkdir($dir2, 0777);
                }
            }
        }
        // exec('chmod -R 777 /var/www/wxshop.shjieta.com.cn/logs');

        $lfile=$lfile.'/wechat';
        $today = date('Y-m-d');
        $fp = fopen($lfile . '_' . $today . '.log', 'a') or exit("Can't open $lfile!");

        $time = date('H:i:s');
        fwrite($fp, "$time  $message\n");
    }


}

?>